﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;

namespace WindowsFormsApplication1.RebornLogin
{
    public class LoginManager
    {
        private List<LoginStatusRequest> loginStatusRequests = new List<LoginStatusRequest>();

        public void Login(string Email, string Password, string clientId, ClientSocketData client, string version)
        {
            client.killLoginTimout();

            if (client.UserID != 0)
                return;

            //Empty version means the client is to old to tell the user they need to update. We have to let them connect.
            if (version != "7.77" && version != "")
            {
                // Send back the reply to the client
                string replyMsg = "<login_result LoginStatus=\"BadVersion\" UserID=\"" + "-1" + "\" />";

                client.socket.BeginSend(replyMsg);
                client.socket.Close();

                return;
            }

            LoginStatusRequest lsr2 = new LoginStatusRequest(client);

            //Check if this socket has an outstanding login request
            lock (loginStatusRequests)
            {
                foreach (LoginStatusRequest lsr in loginStatusRequests)
                {
                    if (lsr.clientSocketData.socket == client.socket)
                        return;
                }
                loginStatusRequests.Add(lsr2);
            }

            //Pass their credentials to the database and retrieve their userId
            //The database will return:
            //  0 if the login is invalid
            //  +userId if the login was successfull
            //  -userId if logged in using reset password credentials

            SqlConnection MyCon = new SqlConnection(PrimaryServer.connectionString);
            SqlCommand SQLCom = new SqlCommand("Users_Login", MyCon);
            SQLCom.CommandType = System.Data.CommandType.StoredProcedure;

            SQLCom.Parameters.AddWithValue("@Email", Email);
            SQLCom.Parameters.AddWithValue("@Password", Password);

            SQLCom.Parameters.Add("@GM", SqlDbType.Bit);
            SQLCom.Parameters["@GM"].Direction = ParameterDirection.Output;

            SQLCom.Parameters.Add("@Return_Value", System.Data.SqlDbType.Int);
            SQLCom.Parameters["@Return_Value"].Direction = System.Data.ParameterDirection.ReturnValue;
            MyCon.Open();
            SQLCom.ExecuteNonQuery();
            MyCon.Close();
            MyCon.Dispose();

            int UserID = 0;
            if (SQLCom.Parameters["@Return_Value"].Value != DBNull.Value)
                UserID = int.Parse(SQLCom.Parameters["@Return_Value"].Value.ToString());

            bool gm = false;
            if (SQLCom.Parameters["@GM"].Value != DBNull.Value)
                gm = bool.Parse(SQLCom.Parameters["@GM"].Value.ToString());

            client.isGM = gm;

            //Invalid Credentials (Notify client, drop connection, and remove pending login request)
            if (UserID == 0)
            {
                try
                {
                    // Send back the reply to the client
                    string replyMsg = "<login_result LoginStatus=\"Invalid\" UserID=\"" + UserID + "\" />";


                    client.socket.BeginSend(replyMsg);
                    client.socket.Close();
                }
                catch { }
                finally
                {
                    lock (loginStatusRequests)
                    {
                        loginStatusRequests.Remove(lsr2);
                    }
                }
                return;
            }

            //Check if we got a negative from the database (this means they logged in using reset password credentials)
            if (UserID < 0)
            {
                //Set flag so we can prompt the user to change their password
                lsr2.usedResetPassword = true;

                //Change the userID back to a positive number (their actual userID)
                UserID = UserID * -1;
            }

            //Check if the user is already on the login server
            if (Managers.socketConnectionsManager.ClientExists(UserID))
            {
                //Send failure, close connection, remove pending login request
                try
                {
                    // Send back the reply to the client
                    string replyMsg = "<login_result LoginStatus=\"UserLoggedIn\" UserID=\"" + UserID + "\" />";

                    client.socket.BeginSend(replyMsg);
                    client.socket.Close();

                    
                }
                catch { }
                finally
                {
                    lock (loginStatusRequests)
                    {
                        loginStatusRequests.Remove(lsr2);
                    }
                }

                return;
            }

            //Assign the clients UserID
            client.UserID = UserID;

            //So far we're successfull, ensure they have a client ID before checking the world servers.
            if (clientId == "")
            {
                MyCon = new SqlConnection(PrimaryServer.connectionString);
                SQLCom = new SqlCommand("LastClientID_SelectAndIncrement", MyCon);
                SQLCom.CommandType = System.Data.CommandType.StoredProcedure;

                SQLCom.Parameters.Add("@Return_Value", System.Data.SqlDbType.Int);
                SQLCom.Parameters["@Return_Value"].Direction = System.Data.ParameterDirection.ReturnValue;
                
                MyCon.Open();
                SQLCom.ExecuteNonQuery();
                MyCon.Close();
                MyCon.Dispose();

                if (SQLCom.Parameters["@Return_Value"].Value != DBNull.Value)
                    clientId = SQLCom.Parameters["@Return_Value"].Value.ToString();
            }

            //Check if the client (machine) is already on the login server, dont check this if we're logging in on a GM account
            if (gm == false && Managers.socketConnectionsManager.ClientExists(clientId))
            {
                //Send failure, close connection, remove pending login request
                try
                {
                    // Send back the reply to the client
                    string replyMsg = "<login_result LoginStatus=\"ClientLoggedIn\" UserID=\"" + UserID + "\" />";

                    client.socket.BeginSend(replyMsg);
                    client.socket.Close();

                    return;
                }
                catch { }
                finally
                {
                    lock (loginStatusRequests)
                    {
                        loginStatusRequests.Remove(lsr2);
                    }
                }
                return;
            }

            client.clientID = clientId;

            //Query Other Servers
            List<ServerSocketData> servers = Managers.socketConnectionsManager.GetServerSocketsCpy();
            lsr2.sendRequests(servers);
        }

        public void processLoginStatusResult(ServerSocketData ssd, int UserID, string clientExistsResult)
        {
            lock (loginStatusRequests)
            {
                foreach (LoginStatusRequest lsr in loginStatusRequests)
                {
                    if (lsr.clientSocketData.UserID == UserID)
                    {
                        lsr.processResult(ssd, clientExistsResult);
                        break;
                    }
                }
            }
        }

        public void requestComplete(LoginStatusRequest request)
        {
            try
            {
                string status = "";
                if (request.result == "userfound")
                    status = "UserLoggedIn";
                else if (request.result == "clientfound" && request.clientSocketData.isGM == false)
                    status = "ClientLoggedIn";
                else if (request.usedResetPassword == true)
                    status = "ResetPassword";

                // Send back the reply to the client
                string replyMsg = "<login_result LoginStatus=\"" + status + "\" UserID=\"" + request.clientSocketData.UserID.ToString() + "\" clientid=\"" + request.clientSocketData.clientID.ToString() + "\" />";

                request.clientSocketData.socket.BeginSend(replyMsg);

                if (status == "UserLoggedIn" || status == "ClientLoggedIn")
                    request.clientSocketData.socket.Close();
            }
            catch { }
            finally
            {
                lock (loginStatusRequests)
                {
                    loginStatusRequests.Remove(request);
                }
            }
        }


        public void MapNames_Select(ClientSocketData csd)
        {
            try
            {
                DataSet DS = new DataSet("MyData");
                SqlConnection MyCon = new SqlConnection(PrimaryServer.connectionString);
                SqlCommand SQLCom = new SqlCommand("MapNames_SelectAll", MyCon);
                SQLCom.CommandType = System.Data.CommandType.StoredProcedure;

                SqlDataAdapter SDA = new SqlDataAdapter(SQLCom);
                SDA.Fill(DS, "AllData");

                string result = DS.GetXml().Replace("MyData", "selectmapnames_result");

                byte[] byData = System.Text.Encoding.ASCII.GetBytes(result + "\0");
                csd.socket.BeginSend(result);
            }
            catch { }
        }

        public void Characters_Select(ClientSocketData csd)
        {
            try
            {
                DataSet DS = new DataSet("MyData");

                SqlConnection MyCon = new SqlConnection(PrimaryServer.connectionString);
                SqlCommand SQLCom = new SqlCommand("Characters_Select", MyCon);
                SQLCom.CommandType = System.Data.CommandType.StoredProcedure;
                SQLCom.Parameters.AddWithValue("@UserID", csd.UserID);

                SqlDataAdapter SDA = new SqlDataAdapter(SQLCom);
                SDA.Fill(DS, "AllData");

                string result = DS.GetXml().Replace("MyData", "selectcharacters_result");

                csd.socket.BeginSend(result);
            }
            catch { }
        }

        public void Characters_Insert(ClientSocketData csd, string Name, string CorpseImageURL, string ImageURL, 
            string Gender, int Strength, int Dexterity, int Agility, int Intelligence, int Wisdom, string MeleeSkill)
        {
            try
            {
                SqlConnection MyCon = new SqlConnection(PrimaryServer.connectionString);
                SqlCommand SQLCom = new SqlCommand("Characters_Insert", MyCon);
                SQLCom.CommandType = System.Data.CommandType.StoredProcedure;

                SQLCom.Parameters.AddWithValue("@UserID", csd.UserID);
                SQLCom.Parameters.AddWithValue("@Name", Name);
                SQLCom.Parameters.AddWithValue("@ImageURL", ImageURL);
                SQLCom.Parameters.AddWithValue("@CorpseImageURL", CorpseImageURL);
                SQLCom.Parameters.AddWithValue("@Gender", Gender);
                SQLCom.Parameters.AddWithValue("@Strength", Strength);
                SQLCom.Parameters.AddWithValue("@Dexterity", Dexterity);
                SQLCom.Parameters.AddWithValue("@Agility", Agility);
                SQLCom.Parameters.AddWithValue("@Intelligence", Intelligence);
                SQLCom.Parameters.AddWithValue("@Wisdom", Wisdom);
                SQLCom.Parameters.AddWithValue("@MeleeSkill", MeleeSkill);

                SQLCom.Parameters.Add("Result", System.Data.SqlDbType.Int);
                SQLCom.Parameters["Result"].Direction = System.Data.ParameterDirection.ReturnValue;
                MyCon.Open();
                SQLCom.ExecuteNonQuery();
                MyCon.Close();
                MyCon.Dispose();

                int result = int.Parse(SQLCom.Parameters["Result"].Value.ToString());

                string replyMsg = "<charactersinsert_result>" + result.ToString() + "</charactersinsert_result>";

                csd.socket.BeginSend(replyMsg);
            }
            catch { }
        }



        public void Characters_Delete(ClientSocketData csd, int ID)
        {
            try
            {
                SqlConnection MyCon = new SqlConnection(PrimaryServer.connectionString);
                SqlCommand SQLCom = new SqlCommand("Characters_Delete", MyCon);
                SQLCom.CommandType = System.Data.CommandType.StoredProcedure;

                SQLCom.Parameters.AddWithValue("@ID", ID);

                MyCon.Open();
                SQLCom.ExecuteNonQuery();
                MyCon.Close();
                MyCon.Dispose();

                string replyMsg = "<charactersdelete_result>" + "true" + "</charactersdelete_result>";

                csd.socket.BeginSend(replyMsg);
            }
            catch { }
        }

        public void CanCreateCharacter(ClientSocketData csd)
        {
            SqlConnection MyCon = new SqlConnection(PrimaryServer.connectionString);
            try
            {
                SqlCommand SQLCom = new SqlCommand("Users_CanCreateCharacter", MyCon);
                SQLCom.CommandType = System.Data.CommandType.StoredProcedure;

                SQLCom.Parameters.AddWithValue("@UserID", csd.UserID);

                SQLCom.Parameters.Add("Result", System.Data.SqlDbType.Int);
                SQLCom.Parameters["Result"].Direction = System.Data.ParameterDirection.ReturnValue;

                MyCon.Open();
                SQLCom.ExecuteNonQuery();


                string replyMsg = "<cancreatecharacter_result>" + SQLCom.Parameters["Result"].Value.ToString() + "</cancreatecharacter_result>";

                csd.socket.BeginSend(replyMsg);
            }
            catch { }
            finally
            {
                MyCon.Close();
                MyCon.Dispose();
            }
        }

        public void Characters_Rename(ClientSocketData csd, int ID, string Name)
        {
            SqlConnection MyCon = new SqlConnection(PrimaryServer.connectionString);
            try
            {
                SqlCommand SQLCom = new SqlCommand("Characters_Rename", MyCon);
                SQLCom.CommandType = System.Data.CommandType.StoredProcedure;

                SQLCom.Parameters.AddWithValue("@ID", ID);
                SQLCom.Parameters.AddWithValue("@Name", Name);

                SQLCom.Parameters.Add("Result", System.Data.SqlDbType.Int);
                SQLCom.Parameters["Result"].Direction = System.Data.ParameterDirection.ReturnValue;

                MyCon.Open();
                SQLCom.ExecuteNonQuery();
                

                string replyMsg = "<charactersrename_result>" + SQLCom.Parameters["Result"].Value.ToString() + "</charactersrename_result>";

                csd.socket.BeginSend(replyMsg);
            }
            catch { }
            finally
            {
                MyCon.Close();
                MyCon.Dispose();
            }
        }

        public void GetCharacterPosition(int CharacterID,out int UserID, out int MapID, out int Instance, out int MapX, out int MapY)
        {
            SqlConnection MyCon = new SqlConnection(PrimaryServer.connectionString);
            SqlCommand SQLCom = new SqlCommand("Characters_SelectByID", MyCon);
            SQLCom.CommandType = System.Data.CommandType.StoredProcedure;
            SQLCom.Parameters.AddWithValue("@ID", CharacterID);

            MyCon.Open();
            SqlDataReader SDR = SQLCom.ExecuteReader();

            MapID = -1;
            Instance = -1;
            MapX = -1;
            MapY = -1;
            UserID = -1;
            while (SDR.Read())
            {
                UserID = int.Parse(SDR["UserID"].ToString());
                MapID = int.Parse(SDR["LastMapID"].ToString());
                Instance = int.Parse(SDR["LastInstance"].ToString());
                MapX = int.Parse(SDR["MapX"].ToString());
                MapY = int.Parse(SDR["MapY"].ToString());
            }
            MyCon.Close();
            MyCon.Dispose();
        }
    }
}
